package ch.sv7.tool.dbmanager.db;

import java.sql.DatabaseMetaData;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import ch.sv7.tool.dbmanager.StringUtil;
import ch.sv7.tool.dbmanager.db.difference.IndexNameDifference;
import ch.sv7.tool.dbmanager.db.difference.IndexQualifierDifference;
import ch.sv7.tool.dbmanager.db.difference.IndexTypeDifference;
import ch.sv7.tool.dbmanager.db.difference.SchemaDifferences;

/**
 * This class represent an index on a database table
 *
 * @author svt
 *
 */
public class IndexSchema implements SchemaElement, Comparable, Identifiable {

	private String name;
	private TableSchema tableSchema;
	
	private boolean unique;
	private String qualifier;
	private String type;
	private String columns;
	private boolean primaryKey;
	private boolean constraint; // particular aspect not handled in comparisons

	

	/**
	 * @param name
	 * @param nonUnique
	 * @param qualifier
	 * @param type
	 * @param columnName
	 */
	public IndexSchema(TableSchema ts, String name, boolean unique, String qualifier, String type, String columns, boolean primaryKey) {
		super();
		this.tableSchema = ts;
		this.name = name;
		this.qualifier = qualifier;
		this.type = type;
		this.unique = unique;
		this.columns = columns;
		//this.columns = StringUtil.sortCommaSeparated(columns);
		this.primaryKey = primaryKey;
	}
	
	/**
	 * @param name
	 * @param nonUnique
	 * @param qualifier
	 * @param type
	 * @param columnName
	 */
	public IndexSchema(TableSchema ts, String name, boolean unique, String qualifier, int type, String columns, boolean primaryKey) {
		super();
		this.tableSchema = ts;
		this.name = name;
		this.qualifier = qualifier;
		this.unique = unique;
		switch (type){
		case DatabaseMetaData.tableIndexClustered: this.type = "clustered"; break;
		case DatabaseMetaData.tableIndexHashed: this.type = "hashed"; break;
		case DatabaseMetaData.tableIndexStatistic: this.type = "statistic"; break;
		case DatabaseMetaData.tableIndexOther: this.type = "other"; break;
		default:this.type = "other"; break;
		}
		this.columns = columns;
		//this.columns = StringUtil.sortCommaSeparated(columns);
		this.primaryKey = primaryKey;
	}
	
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}


	/**
	 * @return the qualifier
	 */
	public String getQualifier() {
		return qualifier;
	}

	/**
	 * @return the type
	 */
	public String getType() {
		return type;
	}

	/**
	 * @return the columnName
	 */
	public String getColumns() {
		return columns;
	}
	
	public Set getColumnSet() {
		return StringUtil.commaSeparatedToSet(columns);
	}

	/**
	 * @see ch.sv7.tool.dbmanager.db.Identifiable#getIdentity()
	 */
	public String getIdentity() {
		return (unique ? "unique" : "non-unique") + ":" + getColumns();
	}

	/**
	 * @return the nonUnique
	 */
	public boolean isUnique() {
		return unique;
	}

	/**
	 * @return the primaryKey
	 */
	public boolean isPrimaryKey() {
		return primaryKey;
	}

	/**
	 * @see ch.sv7.tool.dbmanager.db.Identifiable#getEntityType()
	 */
	public String getEntityType() {
		return "index";
	}

	public String getSchemaIdentifier() {
		return "Index: " + getTableSchema().getName() + "." + getName();
	}

	/**
	 * @return the constraint
	 */
	public boolean isConstraint() {
		return constraint;
	}

	/**
	 * @param constraint the constraint to set
	 */
	public void setConstraint(boolean constraint) {
		this.constraint = constraint;
	}

	/**
	 * @return the tableSchema
	 */
	public TableSchema getTableSchema() {
		return tableSchema;
	}

	public static void mergeIndexes(List indexes) {
		/*
		Collections.sort(indexes, new Comparator() {

			public int compare(Object o1, Object o2) {
				IndexSchema i1 = (IndexSchema)o1;
				IndexSchema i2 = (IndexSchema)o2;
				return i1.getColumns().compareTo(i2.getColumns());
			}
			
		});
		*/
		Map indexByName = new HashMap();
		Iterator iter = indexes.iterator();
		while (iter.hasNext()){
			IndexSchema index = (IndexSchema)iter.next();
			if (indexByName.containsKey(index.getName())){
				IndexSchema originalIndex = (IndexSchema)indexByName.get(index.getName());
				originalIndex.columns = originalIndex.getColumns() + ", " + index.getColumns();
				iter.remove();
			} else {
				indexByName.put(index.getName(), index);
			}
		}
	}
	
	/**
	 * @see ch.sv7.tool.dbmanager.db.Comparable#computeDifferencesFromActual(java.lang.Object)
	 */
	public void computeDifferencesFromActual(SchemaDifferences diffs, Object object) {
		if (!(object instanceof IndexSchema)){
			return;
		}
		IndexSchema actual = (IndexSchema)object;
		/*
		 * No need to process these as they are part of identity

		if (unique != actual.unique) {
			diffs.addDifference(new IndexUnicityDifference(tableSchema, name, " UNIQUE must be " + unique + " but is " + actual.unique));
		}
		if (!qualifier.equals(i.qualifier)) {
			diffs.add(new IndexDifference(tableSchema.getName(),name, " qualifier must be " + qualifier + " but is " + i.qualifier));
		}
		if (!type.equals(i.type)) {
			diffs.addDifference(new IndexDifference(tableSchema, name, " type must be " + type + " but is " + i.type));
		}
		if (!columns.equals(actual.columns)) {
			diffs.addDifference(new AbstractIndexDifference(tableSchema, name, " column name must be " + columns + " but is " + actual.columns));
		}
		*/

		if (!name.equals(actual.name)) {
			diffs.addDifference(this, new IndexNameDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
		if (!type.equals(actual.type)) {
			diffs.addDifference(this, new IndexTypeDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
		if (!qualifier.equals(actual.qualifier)) {
			diffs.addDifference(this, new IndexQualifierDifference(tableSchema, actual.getTableSchema(), this, actual));
		}
		
	}

	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((columns == null) ? 0 : columns.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + (unique ? 1231 : 1237);
		return result;
	}

	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		IndexSchema other = (IndexSchema) obj;
		if (columns == null) {
			if (other.columns != null)
				return false;
		} else if (!columns.equals(other.columns))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (unique != other.unique)
			return false;
		return true;
	}
	
	
}
